1   /*
2    * Copyright (C) 2009 The Guava Authors
3    *
4    * Licensed under the Apache License, Version 2.0 (the "License");
5    * you may not use this file except in compliance with the License.
6    * You may obtain a copy of the License at
7    *
8    * http://www.apache.org/licenses/LICENSE-2.0
9    *
10   * Unless required by applicable law or agreed to in writing, software
11   * distributed under the License is distributed on an "AS IS" BASIS,
12   * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13   * See the License for the specific language governing permissions and
14   * limitations under the License.
15   */
16  
17  package com.google.common.xml;
18  
19  import static com.google.common.escape.testing.EscaperAsserts.assertEscaping;
20  import static com.google.common.escape.testing.EscaperAsserts.assertUnescaped;
21  
22  import com.google.common.annotations.GwtCompatible;
23  import com.google.common.escape.CharEscaper;
24  
25  import junit.framework.TestCase;
26  
27  /**
28   * Tests for the {@link XmlEscapers} class.
29   *
30   * @author Alex Matevossian
31   * @author David Beaumont
32   */
33  @GwtCompatible
34  public class XmlEscapersTest extends TestCase {
35  
36    public void testXmlContentEscaper() throws Exception {
37      CharEscaper xmlContentEscaper = (CharEscaper) XmlEscapers.xmlContentEscaper();
38      assertBasicXmlEscaper(xmlContentEscaper, false, false);
39      // Test quotes are not escaped.
40      assertEquals("\"test\"", xmlContentEscaper.escape("\"test\""));
41      assertEquals("'test'", xmlContentEscaper.escape("'test'"));
42    }
43  
44    public void testXmlAttributeEscaper() throws Exception {
45      CharEscaper xmlAttributeEscaper = (CharEscaper) XmlEscapers.xmlAttributeEscaper();
46      assertBasicXmlEscaper(xmlAttributeEscaper, true, true);
47      // Test quotes are escaped.
48      assertEquals(""test"", xmlAttributeEscaper.escape("\"test\""));
49      assertEquals("'test'", xmlAttributeEscaper.escape("\'test'"));
50      // Test all escapes
51      assertEquals("a"b<c>d&e"f'",
52          xmlAttributeEscaper.escape("a\"b<c>d&e\"f'"));
53      // Test '\t', '\n' and '\r' are escaped.
54      assertEquals("a&#x9;b&#xA;c&#xD;d", xmlAttributeEscaper.escape("a\tb\nc\rd"));
55    }
56  
57    // Helper to assert common properties of xml escapers.
58    private void assertBasicXmlEscaper(CharEscaper xmlEscaper,
59        boolean shouldEscapeQuotes, boolean shouldEscapeWhitespaceChars) {
60      // Simple examples (smoke tests)
61      assertEquals("xxx", xmlEscaper.escape("xxx"));
62      assertEquals("test &amp; test &amp; test",
63          xmlEscaper.escape("test & test & test"));
64      assertEquals("test &lt;&lt; 1", xmlEscaper.escape("test << 1"));
65      assertEquals("test &gt;&gt; 1", xmlEscaper.escape("test >> 1"));
66      assertEquals("&lt;tab&gt;", xmlEscaper.escape("<tab>"));
67  
68      // Test all non-escaped ASCII characters.
69      String s = "!@#$%^*()_+=-/?\\|]}[{,.;:" +
70          "abcdefghijklmnopqrstuvwxyz" +
71          "ABCDEFGHIJKLMNOPQRSTUVWXYZ" +
72          "1234567890";
73      assertEquals(s, xmlEscaper.escape(s));
74  
75      // Test ASCII control characters.
76      for (char ch = 0; ch < 0x20; ch++) {
77        if (ch == '\t' || ch == '\n' || ch == '\r') {
78          // Only these whitespace chars are permitted in XML,
79          if (shouldEscapeWhitespaceChars) {
80            assertEscaping(xmlEscaper, "&#x" + Integer.toHexString(ch).toUpperCase() + ";", ch);
81          } else {
82            assertUnescaped(xmlEscaper, ch);
83          }
84        } else {
85          // and everything else is replaced with FFFD.
86          assertEscaping(xmlEscaper, "\uFFFD", ch);
87        }
88      }
89  
90      // Test _all_ allowed characters (including surrogate values).
91      for (char ch = 0x20; ch <= 0xFFFD; ch++) {
92        // There are a small number of cases to consider, so just do it manually.
93        if (ch == '&') {
94          assertEscaping(xmlEscaper, "&amp;", ch);
95        } else if (ch == '<') {
96          assertEscaping(xmlEscaper, "&lt;", ch);
97        } else if (ch == '>') {
98          assertEscaping(xmlEscaper, "&gt;", ch);
99        } else if (shouldEscapeQuotes && ch == '\'') {
100         assertEscaping(xmlEscaper, "&apos;", ch);
101       } else if (shouldEscapeQuotes && ch == '"') {
102         assertEscaping(xmlEscaper, "&quot;", ch);
103       } else {
104         String input = String.valueOf(ch);
105         String escaped = xmlEscaper.escape(input);
106         assertEquals(
107             "char 0x" + Integer.toString(ch, 16) + " should not be escaped",
108             input, escaped);
109       }
110     }
111 
112     // Test that 0xFFFE and 0xFFFF are replaced with 0xFFFD
113     assertEscaping(xmlEscaper, "\uFFFD", '\uFFFE');
114     assertEscaping(xmlEscaper, "\uFFFD", '\uFFFF');
115 
116     assertEquals("0xFFFE is forbidden and should be replaced during escaping",
117         "[\uFFFD]", xmlEscaper.escape("[\ufffe]"));
118     assertEquals("0xFFFF is forbidden and should be replaced during escaping",
119         "[\uFFFD]", xmlEscaper.escape("[\uffff]"));
120   }
121 }